#include "beditor.h"
#include "ui_beditor.h"

#include "Qsci/qsciapis.h"
#include "Qsci/qsciscintilla.h"
#include "qscibasiclexer.h"

#include <QDataStream>
#include <QDateTime>
#include <QFileDialog>
#include <QMessageBox>
#include <QProcess>
#include <QShortcut>
#include <QTextStream>

#include <QDebug>

// 这个函数可以一次性创建多级目录
QString mkMutiDir(const QString path)
{
    QDir dir(path);
    if (dir.exists(path)) {
        return path;
    }
    QString parentDir = mkMutiDir(path.mid(0, path.lastIndexOf('/')));
    QString dirname = path.mid(path.lastIndexOf('/') + 1);
    QDir parentPath(parentDir);
    if (!dirname.isEmpty())
        parentPath.mkpath(dirname);
    return parentDir + "/" + dirname;
}

void Beditor::Init_Editor()
{
    editor = new QsciScintilla;

    QsciBasicLexer *textLexer = new QsciBasicLexer;
    editor->setLexer(textLexer);
    editor->setMarginType(0, QsciScintilla::NumberMargin);
    editor->setMarginLineNumbers(0, true);
    editor->setMarginWidth(0, 30);

    QsciAPIs *apis = new QsciAPIs(textLexer);

    // keywords
    apis->add(QString("var"));
    apis->add(QString("del"));

    apis->add(QString("if"));
    apis->add(QString("then"));
    apis->add(QString("elif"));
    apis->add(QString("else"));

    apis->add(QString("while"));
    apis->add(QString("for"));
    apis->add(QString("to"));
    apis->add(QString("step"));
    apis->add(QString("break"));
    apis->add(QString("continue"));
    apis->add(QString("return"));

    apis->add(QString("func"));
    apis->add(QString("end"));

    apis->add(QString("and"));
    apis->add(QString("or"));
    apis->add(QString("not"));

    // constant
    apis->add(QString("null"));
    apis->add(QString("true"));
    apis->add(QString("false"));
    apis->add(QString("M_PI"));

    // built-in function
    apis->add(QString("run"));
    apis->add(QString("print"));
    apis->add(QString("prints"));
    apis->add(QString("print_ret"));
    apis->add(QString("input"));
    apis->add(QString("input_num"));
    apis->add(QString("clear"));
    apis->add(QString("is_num"));
    apis->add(QString("is_str"));
    apis->add(QString("is_list"));
    apis->add(QString("is_func"));
    apis->add(QString("len"));
    apis->add(QString("append"));
    apis->add(QString("pop"));
    apis->add(QString("pop_back"));
    apis->add(QString("pop_front"));
    apis->add(QString("extend"));
    apis->add(QString("swap"));

    apis->prepare();

    editor->setAutoCompletionSource(QsciScintilla::AcsAll);
    editor->setAutoCompletionCaseSensitivity(false);
    editor->setAutoCompletionThreshold(1);
    editor->SendScintilla(QsciScintilla::SCI_SETCODEPAGE, QsciScintilla::SC_CP_UTF8);

    QVBoxLayout *mainLayout = new QVBoxLayout;
    mainLayout->addWidget(editor);
    ui->editorWidget->setLayout(mainLayout);
}

void Beditor::Init_ShortCut()
{
    ui->actionSave->setShortcut(QKeySequence("Ctrl+S"));
    ui->actionSaveAs->setShortcut(QKeySequence("Ctrl+Shift+S"));
    ui->actionOpen->setShortcut(QKeySequence("Ctrl+O"));
    ui->actionNew->setShortcut(QKeySequence("Ctrl+N"));
}

Beditor::Beditor(QWidget *parent)
    : QMainWindow(parent)
      , ui(new Ui::Beditor)
{
    ui->setupUi(this);
    Init_Editor();
    Init_ShortCut();

    currentFile = "untitled.txt";
    savePath = "./cache/untitled.txt";
    tempFile = true;
    unsave = false;
    setWindowTitle(currentFile + " - Beditor");
    setWindowIcon(QIcon(":/resource/img/editor.jpg"));

    connect(ui->actionRun, &QAction::triggered, this, &Beditor::runCode);
    ui->actionRun->setIcon(QIcon(":/resource/img/play.jpg"));

    connect(ui->actionOpen, &QAction::triggered, this, &Beditor::openFile);
    ui->actionOpen->setIcon(QIcon(":/resource/img/folder.jpg"));

    connect(ui->actionNew, &QAction::triggered, this, &Beditor::newFile);
    ui->actionNew->setIcon(QIcon(":/resource/img/file.jpg"));

    connect(ui->actionSave, &QAction::triggered, this, &Beditor::saveFile);
    ui->actionSave->setIcon(QIcon(":/resource/img/save.jpg"));

    connect(ui->actionSaveAs, &QAction::triggered, this, &Beditor::saveAsFile);
    ui->actionSaveAs->setIcon(QIcon(":/resource/img/save_as.jpg"));

    connect(ui->actionExit, &QAction::triggered, this, &Beditor::close);
    ui->actionExit->setIcon(QIcon(":/resource/img/close.jpg"));

    connect(ui->actionAbout, &QAction::triggered, this, &Beditor::showAbout);
    connect(editor, &QsciScintilla::textChanged, this, [=]() {
        unsave = true;
        QString title = windowTitle();
        if (title.front() != '*') {
            title.push_front('*');
            setWindowTitle(title);
        }
    });

    mkMutiDir("./cache");
}

Beditor::~Beditor()
{
    // delete the ui will delete everything attached to it
    delete ui;
}

void Beditor::runCode()
{
    if (!QFile("basic.exe").exists()) {
        QMessageBox::critical(this, QStringLiteral("错误"), QStringLiteral("无法找到basic.exe"));
        return;
    }

    saveFile();
    if (tempFile || unsave)
        return;

    // TODO: 这种做法是开启一个分离的cmd进程
    // 主程序与basic进程没有关联
    QStringList args;
    args << "/c";
    args << "start";
    args << "basic.exe";
    args << "-f";
    args << savePath;
    QProcess::startDetached("cmd.exe", args);
}

void Beditor::openFile()
{
    QString filepath = QFileDialog::getOpenFileName(this, "打开", "./");
    if (filepath == "")
        return;

    QFile file(filepath);
    file.open(QIODevice::ReadOnly);
    editor->setText(file.readAll());
    file.close();

    savePath = filepath;
    currentFile = filepath.mid(filepath.lastIndexOf('/') + 1);
    tempFile = false;
    unsave = false;
    setWindowTitle(currentFile + " - Beditor");
}

void Beditor::newFile()
{
    if (tempFile || unsave) {
        if (QMessageBox::No
            == QMessageBox::question(this,
                                     QStringLiteral("警告"),
                                     QStringLiteral("当前文档尚未保存，是否丢弃?"))) {
            saveFile();
        }
    }

    currentFile = "untitled_" + QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss") + ".txt";
    savePath = "./cache/" + currentFile;
    tempFile = true;
    unsave = true;
    editor->clear();
    setWindowTitle(currentFile + " - Beditor");
}

void Beditor::saveFile()
{
    if (tempFile) {
        saveAsFile();
    } else {
        QFile file(savePath);
        file.open(QIODevice::WriteOnly);
        QTextStream out(&file);
        out << editor->text();
        file.close();

        unsave = false;
    }
}

void Beditor::saveAsFile()
{
    QString filepath = QFileDialog::getSaveFileName(this, "另存为", "./untitled.txt");
    if (filepath == "")
        return;

    QFile file(filepath);
    file.open(QIODevice::WriteOnly);
    QTextStream out(&file);
    out << editor->text();
    file.close();

    savePath = filepath;
    currentFile = savePath.mid(savePath.lastIndexOf('/') + 1);
    tempFile = false;
    unsave = false;
    setWindowTitle(currentFile + " - Beditor");
}

void Beditor::showAbout()
{
    QMessageBox msgBox;
    msgBox.setTextFormat(Qt::RichText);
    msgBox.setText("<h1>Beditor v0.1</h1>"
                   "<p>Based on QSciscintilla, compiled using MinGW</p>"
                   "<p>Built on Oct 12 2021 15:05:26</p>"
                   "<p>Copyright 2021-2024 <a href='https://gitee.com/Morphlng'>Morphlng</a>. All "
                   "rights reserved</p>"
                   "<p>This Program is FREE SOFTWARE, DISTRIBUTED UNDER MIT LICENSE</p>");
    msgBox.setWindowTitle(QStringLiteral("About Beditor"));
    msgBox.exec();
}

void Beditor::closeEvent(QCloseEvent *event)
{
    if ((tempFile || unsave) && !(currentFile == "untitled.txt" && editor->text() == "")) {
        if (QMessageBox::No
            == QMessageBox::question(this,
                                     QStringLiteral("警告"),
                                     QStringLiteral("当前文档尚未存储，确认退出？")))
            return;
    }

    return QMainWindow::closeEvent(event);
}
